package com.flying.acls.domain.permission;

import com.flying.acls.model.PermsExpr;
import com.flying.acls.model.PermsType;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class ClassPermissionBuilder {
    private final Map<String, PermsExpr> registeredPermissions = new HashMap<>();

    /**
     * Registers the <tt>Permission</tt> fields from the <tt>BasePermission</tt> class.
     */
    public ClassPermissionBuilder() {
        registerPublicPermissions(CrudPermsExpr.class);
        registerPublicPermissions(HttpPermsExpr.class);
    }

    /**
     * Registers the <tt>Permission</tt> fields from the supplied class.
     */
    public ClassPermissionBuilder(Class<? extends PermsExpr> permissionClass) {
        registerPublicPermissions(permissionClass);
    }

    /**
     * Registers a map of named <tt>Permission</tt> instances.
     *
     * @param namedPermissions the map of <tt>Permission</tt>s, keyed by name.
     */
    public ClassPermissionBuilder(Map<String, ? extends PermsExpr> namedPermissions) {
        for (String name : namedPermissions.keySet()) {
            registerPermission(namedPermissions.get(name), name);
        }
    }

    public void registerPublicPermissions(Class<? extends PermsExpr> clazz) {
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            try {
                Object fieldValue = field.get(null);
                if (PermsExpr.class.isAssignableFrom(fieldValue.getClass())) {
                    // Found a Permission static field
                    PermsExpr perm = (PermsExpr) fieldValue;
                    String permissionName = field.getName();
                    registerPermission(perm, permissionName);
                }
            } catch (Exception e) {
                log.error("error occurs when building permission maps.", e);
            }
        }
    }

    private void registerPermission(PermsExpr perm, String permissionName) {
        String key = perm.getType() + permissionName;
        if (!registeredPermissions.containsKey(key)) registeredPermissions.put(key, perm);
        key = perm.getType() + String.valueOf(perm.getMask());
        if (!registeredPermissions.containsKey(key)) registeredPermissions.put(key, perm);
    }

    public PermsExpr build(PermsType type, int mask) {
        String key = type + String.valueOf(mask);
        if (registeredPermissions.containsKey(key)) {
            // The requested expression has an exact match against a statically-defined
            // Permission, so return it
            return registeredPermissions.get(key);
        }
        PermsExpr permsExpr = PermsExprFactory.get(type, mask);
        registerPermission(permsExpr, String.valueOf(mask));
        return permsExpr;
    }


    private PermsExpr build(PermsType type, String name) {
        PermsExpr permsExpr = registeredPermissions.get(type + name);
        if (permsExpr == null) {
            throw new IllegalArgumentException("Unknow permission. type=" + type + " name = " + name);
        }
        return permsExpr;
    }

    public List<PermsExpr> build(PermsType type, List<String> names) {
        if ((names == null) || (names.size() == 0)) {
            return Collections.emptyList();
        }
        return names.stream().map(name -> build(type, name)).collect(Collectors.toList());
    }

    public List<PermsExpr> build(PermsType type, Object permission) {
        if (permission instanceof Integer) {
            return Collections.singletonList(build(type, ((Integer) permission).intValue()));
        }

        if (permission instanceof PermsExpr) {
            return Collections.singletonList((PermsExpr) permission);
        }

        if (permission instanceof PermsExpr[]) {
            return Arrays.asList((PermsExpr[]) permission);
        }

        if (permission instanceof String) {
            String permString = (String) permission;
            PermsExpr p;
            try {
                p = build(type, permString);
            } catch (IllegalArgumentException notfound) {
                p = build(type, permString.toUpperCase(Locale.ENGLISH));
            }
            if (p != null) return Collections.singletonList(p);
        }
        throw new IllegalArgumentException("Unsupported permission: " + permission);
    }
}